Ruby module: Include、extend 和 prepend


Posted by Wendy on 2021-08-15

前言

Ruby除了可以利用繼承(inheritance)來組織程式碼,讓程式可重複使用外,我們也可以利用引入module的方式讓class更加有彈性,這篇文章就是比較三種方式 include extend prepend有什麼差別。

class的繼承鏈

在開始之前,我們先來了解一般class的繼承鏈吧,我們可以用 ancestors 這個method來查看:

class Animal
end

Animal.ancestors
=> [Animal, Object, Kernel, BasicObject]

include

當使用 include 引入module時,會將module所有的methods變成class的instance methods

module Ability
  def eat
    puts "Eat food."
  end
end

class Animal
  include Ability
end

dog = Animal.new
dog.eat
=> Eat food.

這時候,我們來看看繼承鏈有什麼變化

Animal.ancestors
=> [Animal, Ability, Object, Kernel, BasicObject]

可以看到,Ability是在Animal的上一層,所以,如果在Animal中也有相同名字的method時,會直接執行Animal中的method,那如果同時也要使用module的method,就必須用 super 來執行

class Animal
  include Ability

  def eat
    puts "Animal eat."
  end
end

Animal.new.eat
=> Animal eat.

class Animal
  include Ability

  def eat
    puts "Animal eat."
    super
  end
end

Animal.new.eat
=> Animal eat.
Eat food.

extend

extend 則是會將module所有的methods變成class method

module Ability
  def eat
    puts "Eat food."
  end
end

class Animal
  extend Ability
end

Animal.eat
=> Eat food.

這時候的繼承鍊沒有變化,但如果我們去觀察 singleton_class 的繼承鍊,就可以看出端倪了

Animal.ancestors
=> [Animal, Object, Kernel, BasicObject]

Animal.singleton_class.ancestors
=> [#<Class:Animal>, Ability, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]

prepend

prependinclude 有點像,都是讓class可以引用module的method當作instance method

module Ability
  def eat
    puts "Eat food."
  end
end

class Animal
  prepend Ability
end

Animal.new.eat
=> Eat food.

那他們差別在哪裡呢?其實是繼承鍊的順序

Animal.ancestors
=> [Ability, Animal, Object, Kernel, BasicObject]

這次可以看到 Ability 是在 Animal 的前面,這就會影響到如果有相同method時,哪一個method會執行的優先順序,還有如果兩個要都執行時,該把 super 放在哪裡

module Ability
  def eat
    puts "Eat food."
  end
end

class Animal
  prepend Ability

  def eat
    puts "Animal eat."
  end
end

Animal.new.eat
=> Eat food.

# super 放置的位置要改放在module
module Ability
  def eat
    puts "Eat food."
    super
  end
end

class Animal
  include Ability

  def eat
    puts "Animal eat."
    super
  end
end

Animal.new.eat
=> Eat food.
Animal eat.

以上就是很簡單的比較一下三種引用module的方法,也幫助我自己釐清一些觀念。


#ruby #module #include #extend #prepend







Related Posts

彈出視窗 Modal

彈出視窗 Modal

The introduction and difference between class component and function component in React

The introduction and difference between class component and function component in React

2021動物科學系的生物資訊職涯發展

2021動物科學系的生物資訊職涯發展


Comments